﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using DimensionsHelper.Common.Contracts.DataCollector;
using DimensionsHelper.Common.Contracts.DataCollector.Options;
using DimensionsHelper.Common.Contracts.Excel;
using DimensionsHelper.Common.Contracts.Services;
using DimensionsHelper.Common.Utils;
using DimensionsHelper.Services.DataCollector.Options;

#if NET8_0_OR_GREATER
using System.Diagnostics.CodeAnalysis;
// ReSharper disable RedundantSuppressNullableWarningExpression
#endif

namespace DimensionsHelper.Services.DataCollector;

public sealed class ExcelDataTreeReader : IExcelReader
{
    private const int RowSkipFlag = 0x1;


    private static readonly GroupType UndefinedType = new();

    private readonly Dictionary<string, int> _columnMap;

    // state
    private int _currentColumn;
    private IExcelWorksheetOperator? _currentSheet;
    private TableSideRowInfo? _entryRow;
    private SimpleTypeExpression? _fileExpr;
    private OptionHelper _helper;
    private ExcelDataNode? _root;

    private int _index;
    private int _maxColumn;
    private int _maxRow;
    private int _startColumn;
    private int _startRow;
    private int _tableSideColumn;

    public string Section => "ExcelTreeReader";

    public ExcelDataTreeReader(CollectorOptions options, IReportService? reportService)
    {
        var validator = new Validator
        {
            ReportService = reportService
        };
        _helper = new OptionHelper(options, validator);
        _columnMap = new Dictionary<string, int>();
        InitState();
    }


    public ExcelDataTreeReader(IReportService? reportService)
        : this(new CollectorOptions(), reportService)
    {
    }


    public ExcelDataTreeReader(CollectorOptions options)
    {
        var validator = new Validator();
        _helper = new OptionHelper(options, validator);
        _columnMap = new Dictionary<string, int>();
        InitState();
    }


    public IFilePikerService? FilePikerService
    {
        get; set;
    }

    public IReportService? ReportService
    {
        get; set;
    }


    public ExcelDataNode DataNode => _root ??= new ExcelDataNode("__Root", "RootNode");


    public void SetOptions(object options)
    {
        switch (options)
        {
            case OptionHelper optionHelper:
                _helper = optionHelper;
                break;

            case CollectorOptions collectorOptions:
                var validator = new Validator
                {
                    ReportService = ReportService
                };
                _helper = new OptionHelper(collectorOptions, validator);
                break;
        }
    }


    public T GetData<T>()
    {
        if (!typeof(ExcelDataNode).IsAssignableTo(typeof(T)))
        {
            throw new NotSupportedException();
        }

        return (T)Convert.ChangeType(DataNode, typeof(T));
    }


    public void Clear()
    {
        _root = null;
        InitState();
    }

    public void ReadExcel(IExcelWorkbookOperator workbook, int index, string? expression,
        CancellationToken cancellationToken)
    {
        _index = index;

        if (!string.IsNullOrEmpty(expression))
        {
            _fileExpr = SimpleTypeExpressionParser.Parse(expression!);
        }

        ReportService?.Log($"开始读取工作簿：{workbook.FileName}，数据索引={_index}。");

        foreach (var sht in workbook.Worksheets)
        {
            if (cancellationToken.IsCancellationRequested)
            {
                break;
            }

            ReportService?.Log($"开始读取工作表：{sht.Name}。");
            StartSheet(sht);
            ReadSheet();
            ReportService?.Log($"工作表'{sht.Name}'读取完成。");
        }
    }


    public void ReadExcel(IExcelWorkbookOperator workbook, int index, string? expression)
    {
        ReadExcel(workbook, index, expression, CancellationToken.None);
    }

    private void InitState()
    {
        _currentColumn = 1;
        _startRow = 1;
        _startColumn = 1;
        _maxRow = 1;
        _maxColumn = 1;
        _tableSideColumn = 1;
    }

    private void StartSheet(IExcelWorksheetOperator worksheet)
    {
        _startRow = worksheet.StartRow;
        _startColumn = worksheet.StartColumn;
        _maxRow = worksheet.EndRow;
        _maxColumn = worksheet.EndColumn;
        _currentColumn = _startColumn;
        _currentSheet = worksheet;
        _columnMap.Clear();
    }


    private static bool IsCellEmpty(
#if NET8_0_OR_GREATER
            [NotNullWhen(false)] IExcelRangeOperator? cell, [NotNullWhen(false)] out string? text)
#else
        IExcelRangeOperator? cell, out string text)
#endif
    {
        text = null!;

        if (cell == null)
        {
            return true;
        }

        var value = cell.Text.Trim();
        if (string.IsNullOrEmpty(value))
        {
            return true;
        }

        text = value;
        return false;
    }

    private bool IsCellEmpty(int row, int column,
#if NET8_0_OR_GREATER
            [MaybeNullWhen(true)] 
#endif
        out string text)
    {
        return IsCellEmpty(GetCell(row, column), out text);
    }

    private IExcelRangeOperator? GetCell(int row, int column)
    {
        if (_currentSheet != null && row is > 0 and <= ExcelHelper.MaxRow && column is > 0 and <= ExcelHelper.MaxCol)
        {
            return _currentSheet.GetRange(row, column);
        }

        return null;
    }

    private bool TryGetCellInfo(int row, int column, out decimal value, out string numberFormat,
        out ExcelStyleColor fontColor)
    {
        value = 0;
        numberFormat = ExcelDataValue.DefaultNumberFormat;
        fontColor = ExcelStyleColor.Empty;

        if (_currentSheet != null && row > 0 && row <= ExcelHelper.MaxRow && column > 0 &&
            column <= ExcelHelper.MaxCol)
        {
            var cell = _currentSheet.GetRange(row, column);
            var text = cell.Text;

            if (!string.IsNullOrEmpty(text) && decimal.TryParse(text, out var val))
            {
                value = val;
                numberFormat = cell.NumberFormatLocal;
                fontColor = cell.FontColor;
                return true;
            }
        }

        return false;
    }

    private bool NextTable()
    {
        while (_currentColumn < _maxColumn)
        {
            // +-------+------------+
            // | Empty | Not Empty  |
            // +-------+------------+
            // |       | Type Value |
            // +-------+------------+
            if (IsCellEmpty(_startRow, _currentColumn, out _) &&
                !IsCellEmpty(_startRow, _currentColumn + 1, out _))
            {
                _tableSideColumn = _currentColumn;
                _currentColumn++;
                return true;
            }

            _currentColumn++;
        }

        return false;
    }

    private void ReadTableColumns()
    {
        _columnMap.Clear();

        var cell = GetCell(_startRow, _currentColumn);

        while (!IsCellEmpty(cell, out var text) && _currentColumn <= _maxColumn)
        {
            if (_helper.IsColumnKeyTypeValue(text))
            {
                _columnMap.SetValue(_helper.GetColumnFinalText(text), _currentColumn);
            }

            _currentColumn++;
            cell = GetCell(_startRow, _currentColumn);
        }
    }

    private static bool IsGroupTypeUndefined(GroupType type)
    {
        return ReferenceEquals(type, UndefinedType);
    }

    /// <summary>
    ///     检查非空类型和入口类型，如果配置了入口类型，则入口类型不能为空。
    ///     同时，入口类型不能出现多次。
    /// </summary>
    private bool CheckNotNullTypesAndEntryType(out List<TableSideRowInfo> tableSide)
    {
        _entryRow = null;
        tableSide = [];
        // 同时检查非空类型，如果不包含非空类型，跳过此表格。
        var notNullTypes = new List<GroupType>(_helper.NotNullTypes);

        // 对于行嵌套中出现未定义的非值类型时，应当注意区分：
        // [Definition 1]
        //      [Value 1]
        //      [Value 2]
        //        ...
        // [Definition 2]
        //      [Value 1]
        //      [Value 2]
        //        ...
        // [Undefined  1]     <-    此处出现未定义的类型时，如果照常遍历，会将不属于的行
        //      [Value 1]           添加到嵌套行中。
        //      [Value 2]           因此，在读取行信息时，对于未定义的行也应当进行读取和标记。
        //        ...
        // 
        //

        for (var r = _startRow + 1; r <= _maxRow; r++)
        {
            var cellText = GetCell(r, _tableSideColumn)?.Text;
            var nextText = GetCell(r, _tableSideColumn + 1)?.Text;

            // 跳过空白行
            if (string.IsNullOrEmpty(cellText))
            {
                continue;
            }

            if (!_helper.TryGetTypeFromValue(cellText!, out var groupType))
            {
                // 添加一个未定义的行
                tableSide.Add(new TableSideRowInfo(r, cellText!, UndefinedType, !string.IsNullOrEmpty(nextText)));
                continue;
            }

            var rowInfo = new TableSideRowInfo(r, cellText!, groupType, !string.IsNullOrEmpty(nextText));
            tableSide.Add(rowInfo);

            var index = notNullTypes.IndexOf(groupType);
            // 检查非空类型，存在的话，从列表中移除
            if (index >= 0)
            {
                notNullTypes.RemoveAt(index);
            }

            // 检查入口类型
            if (_helper.EntryType == groupType)
            {
                // 如果入口类型已经出现过了，报错
                if (_entryRow != null)
                {
                    ReportService?.Error($"入口类型'{_helper.EntryType.Name}'只能出现一次，在行'{r}'处重复出现。");
                    return false;
                }

                _entryRow = rowInfo;
            }
        }

        return notNullTypes.Count == 0 && (_helper.EntryType == null || _entryRow != null);
    }


    /// <summary>
    ///     遍历表侧，嵌套行头单元格所处类型定义
    /// </summary>
    private TableSideNesting NestGroupTypes(List<TableSideRowInfo> tableSide)
    {
        TableSideNesting nesting = new(null!);
        Stack<TableSideRowHeader> rowStack = new();
        TableSideNesting? current = null;
        TableSideRowHeader? entryHeader = null;
        TableSideRowInfo? lastBeforeUndefined = null;

        // 首先添加入口类型
        if (_entryRow != null)
        {
            var entryCell = GetCell(_entryRow.Row, _tableSideColumn);

            if (entryCell != null && !string.IsNullOrEmpty(entryCell.Text) &&
                _helper.TryGetTypeFromValue(entryCell.Text, out var entryType) &&
                _helper.TryGetTypeValue(_entryRow.GroupType.Name, entryCell.Text, out var entryValue))
            {
                current = nesting.GetOrAddNextLevel(entryType);
                entryHeader = current.AddRow(_entryRow.Row, entryValue);
                rowStack.Push(entryHeader);
            }
        }

        //  [T1- Value]
        //  [T2- Value]
        //  [Undefined]  <-  出现未定义的类型时，后续行可能会出现错误的嵌套
        //  [T3- Value]
        //      ...
        foreach (var rowInfo in tableSide)
        {
            var r = rowInfo.Row;
            var groupType = rowInfo.GroupType;

            // 跳过未定义的行
            if (IsGroupTypeUndefined(groupType))
            {
                // 如果行带有数据：
                //      [RowHeader] [Data1] ...
                // 则直接跳过。
                if (!rowInfo.IsDataRow)
                {
                    lastBeforeUndefined = null;
                }

                continue;
            }

            // 对于查找到的不保存数据的嵌套行，记录 lastBeforeUndefined
            if (!rowInfo.IsDataRow)
            {
                lastBeforeUndefined = rowInfo;
            }

            if ((_entryRow != null && _entryRow.Row == r) ||
                !_helper.TryGetTypeValue(rowInfo.GroupType.Name, rowInfo.Text, out var rowVal))
            {
                continue;
            }

            if (current == null)
            {
                current = nesting.GetOrAddNextLevel(groupType);
            }
            // 当前已有嵌套时，在类别不同时，应当查找之前是否已经出现过
            // Stack [
            //         R1,
            //         R2, <- 检查是否是之前已有的行，        
            //         R3,    1. 如果已有，需要查找是否在之前已经出现。
            //         R4     2. 如果没有，添加到后一嵌套级别的行。
            //            ]
            else if (current.TypeObj != groupType)
            {
                // 查找 groupType 的父类型
                var prev = current.FindPrevious(t => t.TypeObj == groupType);
                // 如果类型在前方出现过，回退到那个类型
                current = prev ?? current.GetOrAddNextLevel(groupType);
            }

            if (lastBeforeUndefined == null && current.ContainsRow(rowVal))
            {
                continue;
            }

            // 添加一个新行
            var thisRow = current.AddRow(r, rowVal);
            thisRow.IsDataRow = rowInfo.IsDataRow;

            // 只有不存储值的类型，才可以嵌套
            if (groupType.NoValue)
            {
                // 如果有入口类型，并且还没有被引用，将第一个无值类型数据引用到入口类型
                if (entryHeader is { IsReferenced: false })
                {
                    thisRow.DataReference = entryHeader;
                }

                // 如果之前已有，依次出栈
                if (rowStack.Any(row => row.Nesting.TypeObj == groupType))
                {
                    while (rowStack.TryPop(out var row))
                    {
                        if (row.Nesting.TypeObj == groupType)
                        {
                            break;
                        }
                    }
                }

                // 加入下级行
                if (rowStack.TryPeek(out var lastLevel))
                {
                    lastLevel.AddNextLevelRow(thisRow);
                }

                // 嵌套到下一级别
                rowStack.Push(thisRow);
            }
            else if (rowStack.TryPeek(out var lastLevel))
            {
                lastLevel.AddNextLevelRow(thisRow);
            }
        }

        return nesting;
    }


    /// <summary>
    ///     挨个读取整个表格的行头单元格。
    /// </summary>
    /// <returns></returns>
    private bool TryPrepareRowHeaders(
#if NET8_0_OR_GREATER
            [MaybeNullWhen(false)] 
#endif
        out TableSideNesting nesting)
    {
        nesting = null!;

        // 不包含非空类型时，跳过表格
        if (!CheckNotNullTypesAndEntryType(out var tableSide))
        {
            return false;
        }

        nesting = NestGroupTypes(tableSide);
        return true;
    }


    private void ReadColumnKeys(ExcelDataNode node, int row, bool aggregate)
    {
        foreach (var item in _columnMap)
        {
            if (!TryGetCellInfo(row, item.Value, out var val, out var fmt, out var color))
            {
                continue;
            }

            var dataNode = node;

            if (!aggregate)
            {
                dataNode = node.GetChild(_helper.ColumnKeyType.Name, item.Key)
                           ?? node.AddChild(_helper.ColumnKeyType.Name, item.Key);
            }

            // 后添加的值覆盖已有值
            dataNode.Data.SetValue(_index, val, fmt, color);
        }
    }

    // 对于文件表达式，在读取表侧时，影响的是数据是否读取，而不是表格是否跳过。
    // 读取嵌套时，已经检查过入口类型了，此方法内不用再考虑。
    private void ReadNesting(TableSideNesting nesting)
    {
        // 无值类型可能会带有入口类型的数据行，此处直接遍历已有行即可。
        foreach (var header in nesting.Rows)
        {
            // 被引用的行不读取
            if (header.IsReferenced)
            {
                continue;
            }

            // 上一级的行被跳过的话，这一行也要跳过
            if ((header.PreviousLevel != null && header.PreviousLevel.HasFlag(RowSkipFlag)) ||
                (_fileExpr != null && !_fileExpr.Exec(nesting.TypeObj.Name, header.Text)))
            {
                header.AppendFlag(RowSkipFlag);
                continue;
            }

            // 读取时优先考虑数据引用行。
            var readHeader = header.DataReference ?? header;
            var node = DataNode.GetChild(header.Path);
            ReadColumnKeys(node, readHeader.Row, readHeader.Nesting.TypeObj.Aggregate);
        }

        // 如果跳过无值嵌套，则其下层嵌套也被一并跳过
        if (nesting.TypeObj.NoValue && _fileExpr != null && !_fileExpr.Exec(nesting.TypeObj.Name))
        {
            return;
        }

        foreach (var level in nesting)
        {
            ReadNesting(level);
        }
    }


    private void ReadTableSide()
    {
        if (!TryPrepareRowHeaders(out var nesting))
        {
            return;
        }

        foreach (var child in nesting)
        {
            ReadNesting(child);
        }
    }

    private void ReadSheet()
    {
        while (NextTable())
        {
            ReadTableColumns();
            ReadTableSide();
        }
    }

    /// <summary>
    ///     存储表格行头内容
    /// </summary>
    [DebuggerDisplay("{Text,nq}")]
    private class TableSideRowHeader : IEnumerable<TableSideRowHeader>
    {
        private readonly List<TableSideRowHeader> _nextLevelRows;
        private TableSideRowHeader? _dataReference;
        private int _flags;

        internal TableSideRowHeader(int row, string text, TableSideNesting nesting)
        {
            Row = row;
            Text = text;
            Nesting = nesting;
            _nextLevelRows = [];
        }


        /// <summary>
        ///     如果当前行带有数据引用行并且引用行不是无值类型或者
        ///     当前行的嵌套类型不是无值类型，返回true。
        /// </summary>
        public bool IsDataRow { get; set; }


        /// <summary>
        ///     行所属的嵌套定义
        /// </summary>
        public TableSideNesting Nesting { get; }


        /// <summary>
        ///     行号，1开始
        /// </summary>
        public int Row { get; }

        /// <summary>
        ///     行头的单元格文本
        /// </summary>
        public string Text { get; }


        /// <summary>
        ///     前一个嵌套级别的具体行
        /// </summary>
        public TableSideRowHeader? PreviousLevel { get; private set; }


        /// <summary>
        ///     当前行是否被引用。
        /// </summary>
        public bool IsReferenced { get; private set; }


        /// <summary>
        ///     获取或设置引用的数据行
        /// </summary>
        public TableSideRowHeader? DataReference
        {
            get => _dataReference;

            set
            {
                if (_dataReference != null)
                {
                    _dataReference.IsReferenced = false;
                }

                _dataReference = value;

                if (_dataReference != null)
                {
                    _dataReference.IsReferenced = true;
                }
            }
        }


        /// <summary>
        ///     获取此行的路径
        /// </summary>
        public List<KeyValuePair<string, string>> Path
        {
            get
            {
                List<KeyValuePair<string, string>> path =
                    [new KeyValuePair<string, string>(Nesting.TypeObj.Name, Text)];

                var prev = PreviousLevel;
                while (prev != null)
                {
                    path.Add(new KeyValuePair<string, string>(prev.Nesting.TypeObj.Name, prev.Text));
                    prev = prev.PreviousLevel;
                }

                path.Reverse();
                return path;
            }
        }


        public IEnumerator<TableSideRowHeader> GetEnumerator()
        {
            return _nextLevelRows.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        /// <summary>
        ///     像当前行头追加一个数值标记。
        /// </summary>
        public void AppendFlag(int flag)
        {
            _flags |= flag;
        }


        /// <summary>
        ///     如果当前行头包含给定标记，返回true。
        /// </summary>
        public bool HasFlag(int flag)
        {
            return (flag & _flags) != 0;
        }


        /// <summary>
        ///     添加一个下级嵌套具体行
        /// </summary>
        public void AddNextLevelRow(TableSideRowHeader rowHeader)
        {
            rowHeader.PreviousLevel = this;
            _nextLevelRows.Add(rowHeader);
        }
    }


    [DebuggerDisplay("{TypeObj.Name,nq}")]
    private class TableSideNesting : IEnumerable<TableSideNesting>
    {
        private readonly List<TableSideNesting> _nextLevelNestingList;

        // 此类嵌套包含的行
        private readonly List<TableSideRowHeader> _rows;

        internal TableSideNesting(GroupType typeObj)
        {
            TypeObj = typeObj;
            NestingLevel = 0;
            _nextLevelNestingList = [];
            _rows = [];
        }


        /// <summary>
        ///     获取当前嵌套类型下的行集合
        /// </summary>
        public IEnumerable<TableSideRowHeader> Rows => _rows;


        /// <summary>
        ///     类型配置对象
        /// </summary>
        public GroupType TypeObj { get; }

        /// <summary>
        ///     当前的嵌套级别
        /// </summary>
        private int NestingLevel { get; set; }

        /// <summary>
        ///     上一级的嵌套
        /// </summary>
        private TableSideNesting? PreviousLevel { get; set; }


        public IEnumerator<TableSideNesting> GetEnumerator()
        {
            return _nextLevelNestingList.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        /// <summary>
        ///     添加下一级的嵌套
        /// </summary>
        public TableSideNesting GetOrAddNextLevel(GroupType groupType)
        {
            var index = _nextLevelNestingList.FindIndex(e => e.TypeObj == groupType);

            if (index >= 0)
            {
                return _nextLevelNestingList[index];
            }

            var nesting = new TableSideNesting(groupType)
            {
                PreviousLevel = this,
                NestingLevel = NestingLevel + 1
            };

            _nextLevelNestingList.Add(nesting);
            return nesting;

        }


        /// <summary>
        ///     向上查找嵌套类型，如果没找到，返回null。
        /// </summary>
        public TableSideNesting? FindPrevious(Predicate<TableSideNesting> predicate)
        {
            var parent = PreviousLevel;

            while (parent != null)
            {
                if (predicate(parent))
                {
                    return parent;
                }

                parent = parent.PreviousLevel;
            }

            return null;
        }


        /// <summary>
        ///     遍历所有下级嵌套
        /// </summary>
        /// <param name="callback">对每个下级嵌套调用的回调函数</param>
        /// <param name="recursion">是否递归遍历所有下级嵌套</param>
        private void ForEachNext(Action<TableSideNesting> callback, bool recursion)
        {
            foreach (var child in _nextLevelNestingList)
            {
                callback(child);
                if (recursion)
                {
                    child.ForEachNext(callback, true);
                }
            }
        }


        /// <summary>
        ///     搜索所有符合断言回调的下级嵌套
        /// </summary>
        /// <param name="predicate">断言回调函数</param>
        /// <param name="recursion">是否递归搜索下级嵌套</param>
        public TableSideNesting[] FindAllNext(Predicate<TableSideNesting> predicate, bool recursion)
        {
            var result = new List<TableSideNesting>();
            ForEachNext(t =>
            {
                if (predicate(t))
                {
                    result.Add(t);
                }
            }, recursion);
            return result.ToArray();
        }


        /// <summary>
        ///     添加一个当前类型的行。
        /// </summary>
        /// <param name="row">行号，1开始</param>
        /// <param name="text">行头文本</param>
        public TableSideRowHeader AddRow(int row, string text)
        {
            var rowHeader = new TableSideRowHeader(row, text, this);
            _rows.Add(rowHeader);
            return rowHeader;
        }


        /// <summary>
        ///     如果已经包含了指定行头的行，返回<see langword="true" />
        /// </summary>
        public bool ContainsRow(string rowHeaderText)
        {
            return _rows.Exists(r =>
            {
                if (TypeObj.IgnoreCase)
                {
                    return r.Text.Equals(rowHeaderText, StringComparison.OrdinalIgnoreCase);
                }

                return r.Text == rowHeaderText;
            });
        }
    }


    private class TableSideRowInfo
    {
        public TableSideRowInfo(int row, string text, GroupType groupType, bool isDataRow)
        {
            Row = row;
            Text = text;
            GroupType = groupType;
            IsDataRow = isDataRow;
        }

        public int Row { get; }
        public string Text { get; }
        public GroupType GroupType { get; }
        public bool IsDataRow { get; }
    }
}